package me.DonglinPu.COMP2039;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class SheetPrediction {
	private static final double LEARNING_RATE = 0.02;
	
	static final String ORIGINAL_TEST_SET = "protein-secondary-structure.test";
	static final String ORIGINAL_TRAINING_SET = "protein-secondary-structure.train";
	
	private static String sheet_train = "SheetTrain.dl";
	private static String sheet_test = "SheetTest.dl";
	private static String sheet_mytest = "SheetMyTest.dl";
	static final String SHEET_FINAL_WEIGHTS = "SheetFinalWeights.dl";
	
	FileInputStream fis;
	DataInputStream dis;
	BufferedReader br;
	FileWriter fw;
	BufferedWriter bw;
	int count = 0;
	private double[] weights = new double[101];
	int[] Xn = new int[101];
	double sum;
	String vals;
	int desired_result;
	String line;
	double p = 0.0;
	double n = 0.0;
	double o = 0.0;
	double u = 0.0;
	double correct = 0.0;
	double incorrect = 0.0;
	double correct_prediction = 0.0;
	
	double ch = 0;//between -1 and +1
	
	public SheetPrediction() throws IOException{
//		Data.writeBinary("h", ORIGINAL_TRAINING_SET, sheet_train);
//		Data.writeBinary("h", ORIGINAL_TEST_SET, sheet_test);
		
			weights = init_weight(weights);
			learn();
			
			System.out.println("==e==");
			
			predict_sheet(weights, sheet_test);
			
			FileWriter abc = new FileWriter(new File("log.txt"), true);
			BufferedWriter cde = new BufferedWriter(abc);
			cde.write("SHEET_correct_prediction;" + correct_prediction + ";");
			cde.write("SHEET_cc;"+calculate_cc(p, n, o, u) + "\r\n");
			cde.close();
			
//			System.out.println("SHEET correct prediction = " + correct_prediction);
//			System.out.println("SHEET cc="+calculate_cc(p, n, o, u));
//			print_final_weights(weights);
//			write_final_weights_to_file();
//			predict_sheet(weights);
//			System.out.println(p + " " + n + " " + o + " " + u);
//			correct_prediction = 100*correct/(correct + incorrect);
	}
	
	private double calculate_cc(double p, double n, double o, double u) {
		// TODO Auto-generated method stub
		double pn = p*n;
		double ou = o*u;
		double pPo = p+o;
		double pPu = p+u;
		double nPo = n+o;
		double nPu = n+u;
		
//		int total_of_pnou = p + n + o + u;
//		int total_of_correct_incorrect = correct + incorrect;
//		
//		System.out.println("PNOU:" + total_of_pnou);
//		System.out.println("CI:" + total_of_correct_incorrect);
		
		double to_be_sqrt = (pPo*pPu*nPo*nPu);
//		System.out.println("to be sqrt = " + to_be_sqrt);
		double top = pn - ou;
//		System.out.println("top = "+top);
		
		return top/Math.sqrt(to_be_sqrt);
	}

	private void write_final_weights_to_file() throws IOException {
		fw = new FileWriter(SHEET_FINAL_WEIGHTS);
		bw = new BufferedWriter(fw);
		for(int i = 0; i < weights.length; i++){
			bw.write(weights[i]+"\r\n");
		}
		bw.close();
	}
	
	
	// new learning algorithm
	double[] currProteinCorrectPredictionPercent = new double[10];
	int currProteinCorrectPredictionPercentIndex = 0;
	int breakLoopAfterThisNumberOfIteration = 0;// 100
	double prevAllCorrectPredictionPercent = 0.0;// predict all TRAIN set
	double currAllCorrectPredictionPercent = 0.0;// predict all TRAIN set
	
	ArrayList<String> currProtein = new ArrayList<String>();
	ArrayList<Integer> currProteinResult = new ArrayList<Integer>();
	boolean endOfProtein = false;
	boolean sameWindow = false;// use when looping on one window
	
	boolean proteinCorrectPredictionChange = true;
	boolean allCorrectPredictionChange = true;
	/*
	 * This method should run only ONCE.
	 */
//	int numberOfTrained = 0;
////	int[] record = new int[]{500, 1000, 5000, 75000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000, 110000, 120000, 130000, 140000, 150000, 160000};
//	int lastRecorded = 0;
//	int currentRecorded = 0;
	public void learn() throws IOException{
//		FileWriter sheetLogProcess = new FileWriter(new File("logSheetTrain.txt"), true);
//		BufferedWriter sheetLogProcessBw  = new BufferedWriter(sheetLogProcess);
		
		fis = new FileInputStream(sheet_train);
		dis = new DataInputStream(fis);
		br = new BufferedReader(new InputStreamReader(dis));
		
		line = br.readLine();
		while(line != null){// Should only run through the whole file ONCE
//			while(true){// Run through A protein
				if(line.equals("==")){
					// Now you have currProtein and currProteinResult
					// Start: Run through all window of this protein
					for(int i = 0; i < currProtein.size(); i++){
						// Run through A window
						while(true){
							if(!sameWindow){
								Xn = get_Xn(currProtein.get(i));
								desired_result = currProteinResult.get(i);	
//								numberOfTrained++;
//								if(numberOfTrained==500 || numberOfTrained==1000 || numberOfTrained==2000 || numberOfTrained==5000){
//									correct = 0.0;
//									incorrect = 0.0;
//									predict_sheet(weights, sheet_train);
//									correct_prediction = correct/(correct+incorrect);
//									sheetLogProcessBw.write(numberOfTrained + ";sheet_correct_train;" + correct_prediction + ";");
//
//									correct = 0;
//									incorrect = 0;
//									predict_sheet(weights, sheet_test);
//									correct_prediction = correct/(correct+incorrect);
//									sheetLogProcessBw.write("sheet_correct_test;" + correct_prediction + ";\r\n");
//								} else if(numberOfTrained >= 10000 && numberOfTrained <= 160000 && numberOfTrained/1000 == 0){
//									correct = 0.0;
//									incorrect = 0.0;
//									predict_sheet(weights, sheet_train);
//									correct_prediction = correct/(correct+incorrect);
//									sheetLogProcessBw.write(numberOfTrained + ";sheet_correct_train;" + correct_prediction + ";");
//
//									correct = 0;
//									incorrect = 0;
//									predict_sheet(weights, sheet_test);
//									correct_prediction = correct/(correct+incorrect);
//									sheetLogProcessBw.write("sheet_correct_test;" + correct_prediction + ";\r\n");
//								}
							}
							sum = calculate_sum(weights, Xn);//calculate sum
							//Algorithm: Wi(t+1) = Wi(t) + LEARNING_RATE*(disired_result - old_sum)*Xi
							weights = calculate_new_weights(weights, desired_result, sum, Xn);
							if(!predict_this_window(weights, Xn, desired_result)){// Run this window again
								sameWindow = true;
//								System.out.println("window wrong prediction");
							} else {// Run next window
//								System.out.println("window correct prediction");
								sameWindow = false;
								break;
							}
						}
					}

					// End: Run through all window
					// Start: Predict the whole protein
					/*
					 *  If this protein if correctly predicted,
					 *  go to next protein, else repeat this protein
					 */
					if(predict_a_protein(weights, currProtein, currProteinResult)){
						breakLoopAfterThisNumberOfIteration = 0;
						currProtein = new ArrayList<String>();
						currProteinResult = new ArrayList<Integer>();
						line = br.readLine();
					}
				} else {
					vals = line.substring(1, 102);//get the values of this line
					desired_result = new Integer(""+line.charAt(line.length()-1));//get desired result of this line
					currProtein.add(vals);
					currProteinResult.add(desired_result);	
					line = br.readLine();
				}
				// End of if line is "=="
//			}
			
		}
		correct = 0.0;
		incorrect = 0.0;
		predict_sheet(weights, sheet_train);
		
//		System.out.println(p + " " + n + " " + o + " " + u);
		correct_prediction = correct/(correct + incorrect);
		System.out.println(correct_prediction);
//		System.out.println("sheet correct prediction = " + correct_prediction);
//		System.out.println("sheet cc="+calculate_cc(p, n, o, u));
		currAllCorrectPredictionPercent = correct_prediction;
		if(Math.abs(currAllCorrectPredictionPercent - prevAllCorrectPredictionPercent) > 0.001){
			prevAllCorrectPredictionPercent = currAllCorrectPredictionPercent;
			learn();
		} else {
//			print_final_weights(weights);
			correct = 0.0;
			incorrect = 0.0;
			p = 0.0;
			n = 0.0;
			o = 0.0;
			u = 0.0;
		}
//		sheetLogProcessBw.close();
	}


	private boolean predict_a_protein(double[] weights,	ArrayList<String> protein, ArrayList<Integer> results) {
		breakLoopAfterThisNumberOfIteration++;
		double sum = 0.0;
		double correct = 0;
		double incorrect = 0;
		
		for(int i = 0; i < protein.size(); i++){
			Xn = get_Xn(protein.get(i));
			sum = calculate_sum(weights, Xn);
			if(sum > 0 && results.get(i) == 1){
				correct++;
			} else if(sum < 0 && results.get(i) == 0){
				correct++;
			} else {
				incorrect++;
			}
		}
		// TODO
		switch(currProteinCorrectPredictionPercentIndex){
		case 0:
			currProteinCorrectPredictionPercent[0] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 1:
			currProteinCorrectPredictionPercent[1] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 2:
			currProteinCorrectPredictionPercent[2] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 3:
			currProteinCorrectPredictionPercent[3] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 4:
			currProteinCorrectPredictionPercent[4] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 5:
			currProteinCorrectPredictionPercent[5] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 6:
			currProteinCorrectPredictionPercent[6] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 7:
			currProteinCorrectPredictionPercent[7] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 8:
			currProteinCorrectPredictionPercent[8] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 9:
			currProteinCorrectPredictionPercent[9] = correct/(correct+incorrect);
			currProteinCorrectPredictionPercentIndex++;
			break;
		case 10:
			currProteinCorrectPredictionPercent[0] = currProteinCorrectPredictionPercent[1];
			currProteinCorrectPredictionPercent[1] = currProteinCorrectPredictionPercent[2];
			currProteinCorrectPredictionPercent[2] = currProteinCorrectPredictionPercent[3];
			currProteinCorrectPredictionPercent[3] = currProteinCorrectPredictionPercent[4];
			currProteinCorrectPredictionPercent[4] = currProteinCorrectPredictionPercent[5];
			currProteinCorrectPredictionPercent[5] = currProteinCorrectPredictionPercent[6];
			currProteinCorrectPredictionPercent[6] = currProteinCorrectPredictionPercent[7];
			currProteinCorrectPredictionPercent[7] = currProteinCorrectPredictionPercent[8];
			currProteinCorrectPredictionPercent[8] = currProteinCorrectPredictionPercent[9];
			currProteinCorrectPredictionPercent[9] = correct/(correct+incorrect);
			if(breakLoopAfterThisNumberOfIteration > 100){
				if(currProteinCorrectPredictionPercent[9] != currProteinCorrectPredictionPercent[8]){
					return true;
				}
			}
			if(currProteinCorrectPredictionPercent[9] != currProteinCorrectPredictionPercent[8]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[8] != currProteinCorrectPredictionPercent[7]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[7] != currProteinCorrectPredictionPercent[6]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[6] != currProteinCorrectPredictionPercent[5]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[5] != currProteinCorrectPredictionPercent[4]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[4] != currProteinCorrectPredictionPercent[3]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[3] != currProteinCorrectPredictionPercent[2]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[2] != currProteinCorrectPredictionPercent[1]){
				return false;
			}
			if(currProteinCorrectPredictionPercent[1] != currProteinCorrectPredictionPercent[0]){
				return false;
			}
//			System.out.println("Number of amino acid="+protein.size());
//			System.out.println("Correct="+correct+" incorrect="+incorrect);
//			System.out.println(count++);
			return true;
		}
		return false;
	}

	private boolean predict_this_window(double[] weights, int[] Xn, int desired_result) {
		double sum = 0.0;
		sum = calculate_sum(weights, Xn);
//		System.out.println(sum);
		if(sum > 0 && desired_result==1){
			return true;
		} else if (sum <= 0 && desired_result==0){
			return true;
		}
		return false;
	}

//	public void learn() throws IOException{
//		weights = new double[101];
//		Xn = new int[101];
//		weights = init_weight(weights);//assign initial weight 0.2
//		for(int i = 0; i < NUMBER_OF_LOOP; i++) loop();
//	}
//
//	private void loop() throws IOException{
//		fis = new FileInputStream(sheet_train);
//		dis = new DataInputStream(fis);
//		br = new BufferedReader(new InputStreamReader(dis));
//		sum = 0;
//		vals = "";//[000....001] - 101 string
//		desired_result = 0;//Desired result: 1 or 0
//		line = br.readLine();
//		while(line != null){
//			while(true){
//				if(line.equals("==")){
//					break;
//				}
//				vals = line.substring(1, 102);//get the values of this line
//				desired_result = new Integer(""+line.charAt(line.length()-1));//get desired result of this line
//				Xn = get_Xn(vals);//assign all values of Xn
//				sum = calculate_sum(weights, Xn);//calculate sum
//				//Algorithm: Wi(t+1) = Wi(t) + LEARNING_RATE*(disired_result - old_sum)*Xi
//				weights = calculate_new_weights(weights, desired_result, sum, Xn);
//				line = br.readLine();
//			}
//			line = br.readLine();
//		}
//		//print_final_weights(weights);
//	}
	
	private void predict_sheet(double[] weights, String file_name) throws IOException {
		FileInputStream fis = new FileInputStream(file_name);
		DataInputStream dis = new DataInputStream(fis);
		BufferedReader br = new BufferedReader(new InputStreamReader(dis));
		
//		FileWriter fw = new FileWriter(sheet_mytest);
//		BufferedWriter bw = new BufferedWriter(fw);

		int[] Xn = new int[101];
		double sum = 0;
		String vals = "";//[000....001] - 101 string
		
		String line = br.readLine();
//		weights = init_weight(weights);//assign initial weight 0.2
		while(line != null){
			while(true){
				if(line.equals("==")){
//					write_end_of_protein(bw);
					break;
				}
				vals = line.substring(1, 102);//get the values of this line
				desired_result = new Integer(""+line.charAt(line.length()-1));//get desired result of this line
				Xn = get_Xn(vals);//assign all values of Xn
				sum = calculate_sum(weights, Xn);//calculate sum
				if(sum > 0){
					if(desired_result == 1){
						p++;
						correct++;
					} else {
						o++;
						incorrect++;
					}
//					bw.write("D="+desired_result+" my=1"+"\r\n");
				} else {
					if(desired_result == 0){
						n++;
						correct++;
					} else {
						u++;
						incorrect++;
					}
//					bw.write("D="+desired_result+" my=0"+"\r\n");
				}
				line = br.readLine();
			}
			line = br.readLine();
		}
//		bw.close();
	}

	private void print_final_weights(double[] weights) {
		for(int i = 0; i < weights.length; i++){
			System.out.println("index: " + (i+1) + " weights:" + weights[i]);
		}
	}

	private void write_end_of_protein(BufferedWriter bw) throws IOException {
		bw.write("END OF PROTEIN\r\n");
	}

	private void write_weights(double[] weights, BufferedWriter bw) throws IOException {
		// TODO Auto-generated method stub
		for(int i = 0; i < weights.length; i++){
			bw.write(weights[i]+" ");
		}
		bw.write("\r\n");
	}

	private int[] get_Xn(String vals) {
		int[] Xn = new int[vals.length()];
		for(int i = 0; i < Xn.length; i++){
			Xn[i] = new Integer(""+vals.charAt(i));
		}
		return Xn;
	}

	private double[] init_weight(double[] weights) {
		for(int i = 0; i < weights.length; i++){
			weights[i] = -0.1;
		}
		return weights;
	}

	private double calculate_sum(double[] weights, int[] Xn) {
		double sum = 0.0;
		for(int i = 0; i < weights.length; i++){
			sum = sum + Xn[i]*weights[i];
		}
		return sum;
	}
	
	private double[] calculate_new_weights(double[] weights, int desired_result, double old_sum, int[] Xn) {
		int network;
		if(old_sum > 0){
			network = 1;
		} else {
			network = 0;
		}
		for(int i = 0; i < weights.length; i++){
			weights[i] = weights[i] + LEARNING_RATE*(desired_result - network)*Xn[i];
		}
		return weights;
	}
}
